﻿/** 
 * Copyright (c) 2001 University of Maryland, College Park, MD 20742, USA
 * Copyright (c) 2005 In4Arch
 *
 *  The MIT License
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of this 
 *  software and associated documentation files (the "Software"), to deal in the Software 
 *  without restriction, including without limitation the rights to use, copy, modify, merge, 
 *  publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons 
 *  to whom the Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all copies 
 *  or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 *  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
 *  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
 *  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
 *  CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
 *  OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors: Benjamin B. Bederson and Martin Wattenberg 2001 
 * http://www.cs.umd.edu/hcil/treemap-history/index.shtml
 * 
 * Author: moedusa moedusa@gmail.com 2005
 * http://moedusa.name/
 *
 * Author: Nikita Shlyakhov shlyakhov@mail.ru 2006
 **/

 import in4arch.alg.treemap.Rect; 
 import in4arch.alg.treemap.TreeModel; 
 import in4arch.alg.treemap.MapModel;
 
/**
 *
 * Calculations on treemap layouts. Currently
 * holds routines for readability and aspect ratios. This is a good
 * place to add future metrics.
 *
 */
class LayoutCalculations {
    
    public static function averageAspectRatio(model:MapModel):Number {
        return _averageAspectRatio(model.getItems());
    }
    
    public static function _averageAspectRatio(m:Array):Number {
        var s:Number = 0;
        var n:Number = m.length;
        if (m==null || n==0) {
            trace("Can't measure aspect ratio.");
            return 0;
        }
        for (var i=0; i<n; i++) {
            s += m[i].getBounds().aspectRatio();
        }
        return s/n;
    }

    public static function getReadability(tree:TreeModel):Number {
        return _getReadability(tree.getLeafModels());
    }
    
    public static function _getReadability(model:Array):Number{
        var weight:Number = 0;
        var r:Number = 0;
        for (var i=0; i<model.length; i++) {
            var n:Number = model[i].getItems().length;
            weight+=n;
            r += n*getMReadability(model[i]);
        }
        return weight==0 ? 1 : r/weight;
    }
    
    /**
     * Compute the readability of the model.
     * Readability is defined by the number of changes in directions rects are layed out in.
     */
    public static function getMReadability(model:MapModel):Number {
	 var numTurns:Number = 0;
	 var prevAngle:Number = 0;
	 var angle:Number = 0;
	 var angleChange:Number = 0;
	 var items:Array = model.getItems();
	 var b1:Rect, b2:Rect;
	 var dx:Number, dy:Number;
	 var readability:Number;
		for (var i=1; i<items.length; i++) {
			b1 = items[getItemIndex(i-1, model)].getBounds();
			b2 = items[getItemIndex(i, model)].getBounds();
			dx = (b2.x + 0.5*b2.w) - (b1.x + 0.5*b1.w);
			dy = (b2.y + 0.5*b2.h) - (b1.y + 0.5*b1.h);
			angle = Math.atan2(dy, dx);
			if (i >= 2) {
			 angleChange = Math.abs(angle - prevAngle);
			 if (angleChange > Math.PI) {
				 angleChange = Math.abs(angleChange - (2 * Math.PI));
			 }
			 if (angleChange > 0.1) { // allow for rounding
				 numTurns++;
			 }
			}
			prevAngle = angle;
		}
	 readability = 1.0 - (numTurns /items.length);
	 return readability;
    }

    public static function getItemIndex(order:Number, map:MapModel):Number {
	 var i:Number;
	 var items:Array = map.getItems();

	 for (i=0; i<items.length; i++) {
	    if (items[i].getOrder() == order) {
		break;
	    }
	 }

	 return i;
    }
	
}
